package dxbridge;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

public class SimpleMapping {

    public static final String SELECT_DATASET = "//dataset";
    /** Line separator */
    public static final String N = System.getProperty("line.separator");

    /* define strings */
    /**
     * define temp-table tt_brw_mstr xml-node-name 'brw_mstr' like brw_mstr.
     */
    String define_tt = "define temp-table tt_%s xml-node-name '%s' like %s.";
    /* define dataset ds_Browser xml-node-name 'xmlroot'
    for tt_brw_mstr, tt_brwf_det, tt_brwt_det, tt_proc_det, tt_prol_det */
    String define_ds = "define dataset ds_%s xml-node-name 'xmlroot' for %s";
    /* data-relation brw_mstr_brwf_det for tt_brw_mstr, tt_brwf_det
    relation-fields (brw_name,brw_name) nested
    data-relation brw_mstr_brwt_det for tt_brw_mstr, tt_brwt_det
    relation-fields (brw_name,brw_name) nested
    data-relation brw_mstr_proc_det for tt_brw_mstr, tt_proc_det
    relation-fields (brw_name,proc_name) nested
    data-relation proc_det_prol_det for tt_proc_det, tt_prol_det
    relation-fields (proc_name,prol_name, proc_proc_name,prol_proc_name) nested.*/
    /**
     * take 5 parameters: t1name, t2name, t1name, t2name, ("t1field,t2field...")
     */
    String define_ds_relation = "data-relation %s-%s for tt_%s, tt_%s relation-fields (%s) nested";
    String dot = ".";

    /* 
    define data-source srcBrwMstr for brw_mstr keys(brw_name).
    define data-source srcBrwfdet for brwf_det keys(brw_name).
    define data-source srcBrwtdet for brwt_det keys(brw_name).
    define data-source srcProcdet for proc_det keys(proc_name).
    define data-source srcProldet for prol_det keys(prol_name, prol_proc_name).
    */
    String define_datasource = "define data-source src_%s for %s keys(%s).";
    /* 
    buffer tt_brw_mstr:attach-data-source(data-source srcBrwMstr:handle).
    buffer tt_brwf_det:attach-data-source(data-source srcBrwfdet:handle).
    buffer tt_brwt_det:attach-data-source(data-source srcBrwtdet:handle).
    buffer tt_proc_det:attach-data-source(data-source srcProcdet:handle).
    buffer tt_prol_det:attach-data-source(data-source srcProldet:handle).
    */
    String define_buffer = "buffer tt_%s:attach-data-source(data-source src_%s:handle).";
    /*dataset ds_Browser:fill().*/
    String fill = "dataset ds_%s:fill().";
    /*
    buffer tt_brw_mstr:detach-data-source().
    buffer tt_brwf_det:detach-data-source().
    buffer tt_brwt_det:detach-data-source().
    buffer tt_proc_det:detach-data-source().
    buffer tt_prol_det:detach-data-source().
     */
    String detach_buffer = "buffer tt_%s:detach-data-source().";
    /*file_name = DYNAMIC-FUNCTION('getParameter' IN SOURCE-PROCEDURE, INPUT 'file_name'). */
    String DVC = "define variable %s as character.";
    String schema_output_folder = "schema_folder = DYNAMIC-FUNCTION('getParameter' IN SOURCE-PROCEDURE, INPUT 'schema_folder').";
    String xml_output_folder = "xml_folder = DYNAMIC-FUNCTION('getParameter' IN SOURCE-PROCEDURE, INPUT 'xml_folder').";

    String write_xml = "dataset ds_%s:write-xml(\"file\",%s,YES,?,?,No,No).";
    String write_schema = "dataset ds_%s:write-xmlschema(\"file\",%s,YES,?,No).";

    String final_return = "if ERROR-STATUS:ERROR then return \"1\". else return \"0\".";

    /* import */
    String ds_def = "dataset ds:read-xml(\"file\", \"%s\", \"EMPTY\", ?, no).";
    String import_buf_copy = "disable triggers for load of %s. \nfor each tt_%s no-lock:\n  create %s. \n  buffer-copy tt_%s to %s. \nend.";

    @SuppressWarnings("unchecked")
    public static SimpleDataSet parse(File file) throws DocumentException {

        Document doc = new SAXReader().read(file);
        SimpleDataSet ds = null;
        List<Node> dsNodes = doc.selectNodes(SELECT_DATASET);
        for (Node dsNode : dsNodes) {
            ds = new SimpleDataSet(dsNode.valueOf("@name"));
            List<Node> tableNodes = dsNode.selectNodes("./table");
            for (Node tNode : tableNodes) {
                SimpleTable table = new SimpleTable(tNode.valueOf("@name"));
                List<Node> keyNodes = tNode.selectNodes("./key");
                for (Node keyNode : keyNodes) {
                    table.addKey(keyNode.getText().trim());
                }
                List<Node> refNodes = tNode.selectNodes("./keyref");
                for (Node refNode : refNodes) {
                    table.addRef(new SimpleKeyref(refNode.valueOf("@name"),
                            refNode.valueOf("@ftable"), refNode
                                    .valueOf("@fkey")));
                }
                ds.addTable(table);
            }
        }
        return ds;
    }

    public boolean progressImportCodeGenerate(SimpleDataSet ds,
            File codeOutputFolder, File dataxml) {

        try {
            StringBuffer sb = new StringBuffer();
            BufferedWriter writer = new BufferedWriter(new FileWriter(new File(
                    codeOutputFolder.getAbsolutePath() + File.separator
                            + ds.getName() + ".import.p")));

            List<SimpleTable> tables = ds.getTables();
            for (SimpleTable t : tables) {
                writer.append(
                        String.format(define_tt, t.getName(), t.getName(), t
                                .getName())).append(N);
                sb.append("tt_").append(t.getName()).append(",");
            }
            writer.append(N);

            writer.append(
                    String.format(define_ds, ds.getName(), sb.subSequence(0, sb
                            .length() - 1))).append(N);
            for (SimpleTable t : tables) {
                List<SimpleKeyref> refs = t.getRefs();
                if (refs != null && refs.size() > 0) {

                    // TODO potential bug, if ref to more than one
                    // table.
                    // but no such situation currently.
                    sb = new StringBuffer();
                    String refTable = null;
                    for (SimpleKeyref ref : refs) {
                        sb.append(ref.getRefkey()).append(",").append(
                                ref.getKey()).append(",");
                        refTable = ref.getReftable();
                    }
                    writer.append(
                            String.format(define_ds_relation, refTable, t
                                    .getName(), refTable, t.getName(), sb
                                    .substring(0, sb.length() - 1))).append(N);
                }
            }
            writer.append(dot).append(N);
            writer.append(N);

            for (SimpleTable t : tables) {
                List<SimpleKeyref> refs = t.getRefs();
                if (refs != null && refs.size() > 0) {
                    sb = new StringBuffer();
                    for (SimpleKeyref ref : refs) {
                        sb.append(ref.getKey()).append(",");
                    }
                    writer.append(
                            String.format(define_datasource, t.getName(), t
                                    .getName(), sb
                                    .substring(0, sb.length() - 1))).append(N);
                } else {
                    sb = new StringBuffer();
                    for (String key : t.getKeys()) {
                        sb.append(key).append(",");
                    }
                    writer.append(
                            String.format(define_datasource, t.getName(), t
                                    .getName(), sb
                                    .substring(0, sb.length() - 1))).append(N);
                }
            }

            writer.append(N);
            writer.append(String.format(ds_def, dataxml.getAbsolutePath()))
                    .append(N);
            for (SimpleTable t : tables) {
                writer.append(
                        String.format(import_buf_copy, t.getName(),
                                t.getName(), t.getName(), t.getName(), t
                                        .getName())).append(N);
            }
            writer.append(final_return);
            writer.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;

    }

    public boolean progressExportCodeGenerate(SimpleDataSet ds,
            File codeOutputFolder) {

        try {
            StringBuffer sb = new StringBuffer();
            BufferedWriter writer = new BufferedWriter(new FileWriter(new File(
                    codeOutputFolder.getAbsolutePath() + File.separator
                            + ds.getName() + ".export.p")));

            List<SimpleTable> tables = ds.getTables();
            for (SimpleTable t : tables) {
                writer.append(
                        String.format(define_tt, t.getName(), t.getName(), t
                                .getName())).append(N);
                sb.append("tt_").append(t.getName()).append(",");
            }
            writer.append(N);

            writer.append(
                    String.format(define_ds, ds.getName(), sb.subSequence(0, sb
                            .length() - 1))).append(N);
            for (SimpleTable t : tables) {
                List<SimpleKeyref> refs = t.getRefs();
                if (refs != null && refs.size() > 0) {

                    // TODO potential bug, if ref to more than one
                    // table.
                    // but no such situation currently.
                    sb = new StringBuffer();
                    String refTable = null;
                    for (SimpleKeyref ref : refs) {
                        sb.append(ref.getRefkey()).append(",").append(
                                ref.getKey()).append(",");
                        refTable = ref.getReftable();
                    }
                    writer.append(
                            String.format(define_ds_relation, refTable, t
                                    .getName(), refTable, t.getName(), sb
                                    .substring(0, sb.length() - 1))).append(N);
                }
            }
            writer.append(dot).append(N);
            writer.append(N);

            for (SimpleTable t : tables) {
                List<SimpleKeyref> refs = t.getRefs();
                if (refs != null && refs.size() > 0) {
                    sb = new StringBuffer();
                    for (SimpleKeyref ref : refs) {
                        sb.append(ref.getKey()).append(",");
                    }
                    writer.append(
                            String.format(define_datasource, t.getName(), t
                                    .getName(), sb
                                    .substring(0, sb.length() - 1))).append(N);
                } else {
                    sb = new StringBuffer();
                    for (String key : t.getKeys()) {
                        sb.append(key).append(",");
                    }
                    writer.append(
                            String.format(define_datasource, t.getName(), t
                                    .getName(), sb
                                    .substring(0, sb.length() - 1))).append(N);
                }
            }
            writer.append(N);
            /* attach data-source */
            for (SimpleTable t : tables) {
                writer.append(
                        String.format(define_buffer, t.getName(), t.getName()))
                        .append(N);
            }

            /* fill */
            writer.append(String.format(fill, ds.getName())).append(N);

            /* detach data-source */
            for (SimpleTable t : tables) {
                writer.append(String.format(detach_buffer, t.getName()))
                        .append(N);
            }
            writer.append(N);
            writer.append(String.format(DVC, "schema_folder")).append(N);
            writer.append(String.format(DVC, "xml_folder")).append(N);

            writer.append(String.format(schema_output_folder)).append(N);
            writer.append(String.format(xml_output_folder)).append(N);

            writer.append(
                    String.format(write_xml, ds.getName(), "xml_folder + \"/"
                            + ds.getName() + ".xml\"")).append(N);
            writer.append(
                    String.format(write_schema, ds.getName(),
                            "schema_folder + \"/" + ds.getName() + ".xsd\""))
                    .append(N).append(N);

            writer.append(final_return);

            writer.close();

        } catch (IOException e) {
            e.printStackTrace();
        }

        return true;

    }

    public static void main(String args[]) throws DocumentException {
        SimpleMapping sm = new SimpleMapping();
        sm
                .progressExportCodeGenerate(
                        SimpleMapping
                                .parse(new File(
                                        "dataset_def/qaddb/cd_det.xml")),
                        new File("./temp"));
        sm
                .progressImportCodeGenerate(
                        SimpleMapping
                                .parse(new File(
                                        "dataset_def/qaddb/cd_det.xml")),
                        new File("./temp"), new File("abc.p"));
    }

}

class SimpleDataSet {

    String name = null;;
    List<SimpleTable> tables = new LinkedList<SimpleTable>();

    public SimpleDataSet(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void addTable(SimpleTable table) {
        tables.add(table);
    }

    public List<SimpleTable> getTables() {
        return tables;
    }

}

class SimpleTable {

    private List<String> keys = new LinkedList<String>();
    private List<SimpleKeyref> refs = new LinkedList<SimpleKeyref>();

    private String name = null;

    public List<SimpleKeyref> getRefs() {
        return refs;
    }

    public SimpleTable(String name) {
        this.name = name;
    }

    public void addKey(String key) {
        keys.add(key);
    }

    public void addRef(SimpleKeyref ref) {
        refs.add(ref);
    }

    public List<String> getKeys() {
        return keys;
    }

    public String getName() {
        return name;
    }

}

class SimpleKeyref {
    private String key;
    private String reftable;
    private String refkey;

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getReftable() {
        return reftable;
    }

    public void setReftable(String reftable) {
        this.reftable = reftable;
    }

    public String getRefkey() {
        return refkey;
    }

    public void setRefkey(String refkey) {
        this.refkey = refkey;
    }

    public SimpleKeyref(String key, String reftable, String refkey) {
        this.key = key;
        this.refkey = refkey;
        this.reftable = reftable;
    }
}

enum dotpproduct {
    schema_only, xml_only, both
}